<template>
  <div class="scroll-container">
    <div class="data-list">
      <div 
        v-for="item in items" 
        :key="item.id" 
        class="data-item"
        :class="{ 'updated': item.isUpdated }"
      >
        <h3>{{ item.title }}</h3>
        <p>{{ item.content }}</p>
        <div class="meta-info">
          <span>更新时间: {{ formatTime(item.updatedAt) }}</span>
          <span v-if="item.isUpdated" class="updated-badge">刚刚更新</span>
        </div>
      </div>
    </div>
    
    <div v-if="loading" class="loading-indicator">
      <div class="spinner"></div>
      <p>加载中...</p>
    </div>
    
    <div v-if="hasMore === false && items.length > 0" class="end-message">
      已加载全部数据
    </div>
    
    <div v-if="error" class="error-message">
      {{ error }}
      <button @click="loadMore">重试</button>
    </div>
  </div>
</template>

<script>
export default {
  name: 'ScrollPaginationWithPolling',
  data() {
    return {
      items: [],          // 所有数据项
      page: 1,            // 当前页码
      pageSize: 10,       // 每页条数
      loading: false,     // 是否正在加载
      hasMore: true,      // 是否还有更多数据
      error: null,        // 错误信息
      pollInterval: null, // 轮询定时器
      pollIntervalTime: 1000, // 轮询间隔(毫秒) - 30秒
      lastPollTime: null  // 上次轮询时间
    };
  },
  
  mounted() {
    // 初始加载第一页数据
    this.loadMore();
    
    // 监听滚动事件
    window.addEventListener('scroll', this.handleScroll);
    
    // 启动轮询
    this.startPolling();
  },
  
  beforeUnmount() {
    // 移除滚动监听
    window.removeEventListener('scroll', this.handleScroll);
    
    // 清除轮询定时器
    this.stopPolling();
  },
  
  methods: {
    /**
     * 处理滚动事件，判断是否需要加载更多
     */
    handleScroll() {
      // 如果正在加载中或没有更多数据，则不执行
      if (this.loading || !this.hasMore) return;
      
      // 计算滚动位置
      const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
      const scrollHeight = document.documentElement.scrollHeight;
      const clientHeight = document.documentElement.clientHeight;
      
      // 当滚动到距离底部200px时加载更多
      if (scrollTop + clientHeight >= scrollHeight - 200) {
        this.loadMore();
      }
    },
    
    /**
     * 加载更多数据
     */
    async loadMore() {
      this.loading = true;
      this.error = null;
      
      try {
        // 模拟API请求
        const response = await this.fetchData(this.page, this.pageSize);
        
        if (response.data.length < this.pageSize) {
          this.hasMore = false;
        }
        
        // 将新数据添加到列表
        this.items = [...this.items, ...response.data];
        
        // 页码递增
        this.page++;
      } catch (err) {
        this.error = '加载数据失败，请稍后重试';
        console.error('加载数据失败:', err);
      } finally {
        this.loading = false;
      }
    },
    
    /**
     * 轮询检查数据更新
     */
    async checkForUpdates() {
      // 如果没有数据，则不轮询
      if (this.items.length === 0) return;
      
      try {
        // 记录轮询时间
        this.lastPollTime = new Date();
        
        // 获取当前已加载的所有ID
        const itemIds = this.items.map(item => item.id);
        
        // 模拟请求最新数据状态
        const response = await this.fetchUpdates(itemIds);
        
        // 更新数据
        if (response.updatedItems && response.updatedItems.length > 0) {
          this.updateItems(response.updatedItems);
        }
      } catch (err) {
        console.error('轮询更新失败:', err);
      }
    },
    
    /**
     * 更新数据项
     */
    updateItems(updatedItems) {
      this.items = this.items.map(item => {
        const updatedItem = updatedItems.find(ui => ui.id === item.id);
        
        if (updatedItem) {
          // 标记为已更新，并在3秒后移除标记
          const newItem = { ...item, ...updatedItem, isUpdated: true };
          
          setTimeout(() => {
            this.items = this.items.map(i => 
              i.id === item.id ? { ...i, isUpdated: false } : i
            );
          }, 3000);
          
          return newItem;
        }
        
        return item;
      });
    },
    
    /**
     * 启动轮询
     */
    startPolling() {
      // 先清除可能存在的定时器
      this.stopPolling();
      
      // 立即执行一次，然后按间隔执行
      this.checkForUpdates();
      
      // 设置定时器
      this.pollInterval = setInterval(
        () => this.checkForUpdates(), 
        this.pollIntervalTime
      );
    },
    
    /**
     * 停止轮询
     */
    stopPolling() {
      if (this.pollInterval) {
        clearInterval(this.pollInterval);
        this.pollInterval = null;
      }
    },
    
    /**
     * 格式化时间
     */
    formatTime(timestamp) {
      const date = new Date(timestamp);
      return date.toLocaleString();
    },
    
    /**
     * 模拟API请求 - 获取分页数据
     */
    fetchData(page, pageSize) {
      return new Promise((resolve) => {
        // 模拟网络延迟
        setTimeout(() => {
          // 生成模拟数据
          const data = Array.from({ length: pageSize }, (_, i) => ({
            id: (page - 1) * pageSize + i + 1,
            title: `项目 ${(page - 1) * pageSize + i + 1}`,
            content: `这是项目 ${(page - 1) * pageSize + i + 1} 的内容...`,
            updatedAt: new Date(Date.now() - Math.random() * 86400000).toISOString(),
            isUpdated: false
          }));
          
          // 随机决定是否还有更多数据（仅用于演示）
          const hasMore = page < 5;
          
          resolve({ data, hasMore });
        }, 800);
      });
    },
    
    /**
     * 模拟API请求 - 检查数据更新
     */
    fetchUpdates(ids) {
      return new Promise((resolve) => {
        // 模拟网络延迟
        setTimeout(() => {
          // 随机选择一些ID进行更新（仅用于演示）
          const updatedItems = ids
            .filter(() => Math.random() > 0.7) // 30%的概率被更新
            .map(id => ({
              id,
              content: `这是项目 ${id} 的更新内容... ${new Date().toLocaleTimeString()}`,
              updatedAt: new Date().toISOString()
            }));
          
          resolve({ updatedItems });
        }, 500);
      });
    }
  }
};
</script>

<style scoped>
.scroll-container {
  min-height: 100vh;
  padding: 20px;
}

.data-list {
  max-width: 800px;
  margin: 0 auto;
}

.data-item {
  background: #fff;
  border-radius: 8px;
  padding: 20px;
  margin-bottom: 15px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
  transition: all 0.3s ease;
}

.data-item.updated {
  border-left: 4px solid #42b983;
  background-color: #f6ffed;
}

.meta-info {
  display: flex;
  justify-content: space-between;
  margin-top: 10px;
  color: #666;
  font-size: 0.9em;
}

.updated-badge {
  background-color: #42b983;
  color: white;
  padding: 2px 8px;
  border-radius: 12px;
  font-size: 0.8em;
}

.loading-indicator {
  text-align: center;
  padding: 20px;
}

.spinner {
  width: 40px;
  height: 40px;
  margin: 0 auto;
  border: 4px solid #f3f3f3;
  border-top: 4px solid #42b983;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.end-message {
  text-align: center;
  padding: 20px;
  color: #666;
}

.error-message {
  text-align: center;
  padding: 20px;
  color: #e53e3e;
}

.error-message button {
  margin-top: 10px;
  padding: 6px 12px;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
</style>
